﻿using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
using HYCodeBase.Math;
using HYRevitCode.CurveUtility.CurveGroupSuite;
using HYRevitCode.RevitObjectCompareUtility;
using HYRevitCode.XYZUtility;

namespace HYRevitCode.CurveUtility.LineGroupSuite
{
	public static class LineGroupManger
	{
		public static List<Line> OffsetLinesWithLink(List<Line> lstInptLines, double offsetValue, uint inputUsePrecision = 4u, bool ifRemoveReturnNull = false)
		{
			List<Line> result = new List<Line>();
			IEnumerable<Line> source = from n in lstInptLines
			where null != n
			select n;
			List<ILineGeter> lstTempUseBean = new List<ILineGeter>();
			source.ForEach(delegate(Line k)
			{
				lstTempUseBean.Add(new LineGeterBean
				{
					UseLine = k
				});
			});
			List<Line> lstUseBaseLines = new List<Line>();
			lstTempUseBean.ForEach(delegate(ILineGeter k)
			{
				lstUseBaseLines.Add((k as LineGeterBean).CalculateOffsetLine(offsetValue));
			});
			LineGroupManger.ProjectGroupLine(ref lstTempUseBean, inputUsePrecision, lstUseBaseLines, double.MaxValue, null, new bool?(MathUtility.Greaterthan_Or_Equal(offsetValue, 0.0)));
			if (ifRemoveReturnNull)
			{
				result = (from n in lstTempUseBean
				where null != n.UseLine
				select n.UseLine).ToList<Line>();
			}
			else
			{
				result = (from n in lstTempUseBean
				select n.UseLine).ToList<Line>();
			}
			return result;
		}

		public static void AdjustToLinkedLine(ref List<ILineGeter> lstInputLineGeter, uint inputUsePrecision)
		{
			Dictionary<XYZ, ILineGeter> dictionary = LineGroupManger.CalculateNoneLinkEnd(lstInputLineGeter, inputUsePrecision);
			XYZEqualityComparer xyzequalityComparer = new XYZEqualityComparer();
			xyzequalityComparer.SetPrecision(inputUsePrecision);
			for (int i = 0; i < dictionary.Count; i++)
			{
				KeyValuePair<XYZ, ILineGeter> keyValuePair = dictionary.ElementAt(i);
				for (int j = i + 1; j < dictionary.Count; j++)
				{
					KeyValuePair<XYZ, ILineGeter> keyValuePair2 = dictionary.ElementAt(j);
					IntersectionResultArray intersectionResultArray;
                    if (8 == (int)keyValuePair.Value.UseLine.Intersect(keyValuePair2.Value.UseLine, out intersectionResultArray))
					{
						XYZ xyzpoint = intersectionResultArray.get_Item(0).XYZPoint;
						keyValuePair.Value.UseLine = LineGroupManger.AdjustNoneLinkPoint(xyzequalityComparer, ref keyValuePair, xyzpoint);
						keyValuePair2.Value.UseLine = LineGroupManger.AdjustNoneLinkPoint(xyzequalityComparer, ref keyValuePair2, xyzpoint);
					}
				}
			}
		}

		public static void ProjectGroupLine(ref List<ILineGeter> lstInputLineGeter, uint inputUsePrecision, List<Line> lstInputBaseLine, double usePairDistance, double? directionTolerance = null, bool? ifLimitProjectDirection = null)
		{
			List<Line> lstBackBaseLine = lstInputBaseLine.Distinct<Line>().ToList<Line>();
			lstInputLineGeter = lstInputLineGeter.Distinct<ILineGeter>().ToList<ILineGeter>();
			Dictionary<Line, Line> useBaseLineAndUnboundBaseLine = null;
			Dictionary<ILineGeter, Line> projectPair = LineGroupManger.CalculatePorjectLinePair(lstBackBaseLine, lstInputLineGeter, usePairDistance, ref useBaseLineAndUnboundBaseLine, directionTolerance, ifLimitProjectDirection);
			Dictionary<XYZ, List<ILineGeter>> dictionary = LineGroupManger.GroupLines(lstInputLineGeter, inputUsePrecision);
			XYZEqualityComparer xyzequalityComparer = new XYZEqualityComparer();
			xyzequalityComparer.SetPrecision(inputUsePrecision);
			Dictionary<XYZ, XYZ> dictionary2 = new Dictionary<XYZ, XYZ>(xyzequalityComparer);
			foreach (KeyValuePair<XYZ, List<ILineGeter>> keyValuePair in dictionary)
			{
				if (!dictionary2.ContainsKey(keyValuePair.Key))
				{
					dictionary2.Add(keyValuePair.Key, null);
				}
				dictionary2[keyValuePair.Key] = keyValuePair.Key;
			}
			LineGroupManger.CalculateTransformPoints(useBaseLineAndUnboundBaseLine, projectPair, dictionary2);
			foreach (ILineGeter lineGeter in lstInputLineGeter)
			{
				Line useLine;
				try
				{
					XYZ xyz = dictionary2[lineGeter.UseLine.GetEndPoint(0)];
					XYZ xyz2 = dictionary2[lineGeter.UseLine.GetEndPoint(1)];
					useLine = Line.CreateBound(xyz, xyz2);
				}
				catch (Exception)
				{
					useLine = null;
				}
				lineGeter.UseLine = useLine;
			}
		}

		public static bool IfInputIsLinePolygon(List<ILineGeter> lstInputLineGeter, uint inputUsePrecision)
		{
			Dictionary<ICurveGeter, ILineGeter> dictionary;
			return CurveGroupManger.IfInputIsCurvePolygon(LineGroupManger.TransformCurveGeter(lstInputLineGeter, out dictionary), inputUsePrecision);
		}

		public static bool IfInputIsLinkLine(List<ILineGeter> lstInputLineGeter, uint inputUsePrecision)
		{
			Dictionary<ICurveGeter, ILineGeter> dictionary;
			return CurveGroupManger.IfInputIsLinkCurve(LineGroupManger.TransformCurveGeter(lstInputLineGeter, out dictionary), inputUsePrecision);
		}

		public static bool TryOrderLineByLink(List<ILineGeter> lstInputLineGeter, uint inputUsePrecision, out List<KeyValuePair<ILineGeter, bool>> lstOrederValue)
		{
			Dictionary<ICurveGeter, ILineGeter> tempValue;
			List<KeyValuePair<ICurveGeter, bool>> source;
			bool flag = CurveGroupManger.TryOrderCurveByLink(LineGroupManger.TransformCurveGeter(lstInputLineGeter, out tempValue), inputUsePrecision, out source);
			if (flag)
			{
				lstOrederValue = (from n in source
				select new KeyValuePair<ILineGeter, bool>(tempValue[n.Key], n.Value)).ToList<KeyValuePair<ILineGeter, bool>>();
				return flag;
			}
			lstOrederValue = null;
			return flag;
		}

		public static Dictionary<XYZ, ILineGeter> CalculateNoneLinkEnd(List<ILineGeter> lstInputLineGeter, uint inputUsePrecision)
		{
			Dictionary<ICurveGeter, ILineGeter> tempValue;
			return CurveGroupManger.CalculateNoneLinkEnd(LineGroupManger.TransformCurveGeter(lstInputLineGeter, out tempValue), inputUsePrecision).ToDictionary((KeyValuePair<XYZ, ICurveGeter> k) => k.Key, (KeyValuePair<XYZ, ICurveGeter> k) => tempValue[k.Value]);
		}

		public static Dictionary<XYZ, List<ILineGeter>> GroupLines(List<ILineGeter> inputLines, uint inputUsePrecision)
		{
			Dictionary<ICurveGeter, ILineGeter> dictionary2;
			Dictionary<XYZ, List<ICurveGeter>> dictionary = CurveGroupManger.GroupCurves(LineGroupManger.TransformCurveGeter(inputLines, out dictionary2), inputUsePrecision);
			Dictionary<XYZ, List<ILineGeter>> dictionary3 = new Dictionary<XYZ, List<ILineGeter>>();
			foreach (KeyValuePair<XYZ, List<ICurveGeter>> keyValuePair in dictionary)
			{
				List<ILineGeter> list = new List<ILineGeter>();
				dictionary3.Add(keyValuePair.Key, list);
				foreach (ICurveGeter key in keyValuePair.Value)
				{
					list.Add(dictionary2[key]);
				}
			}
			return dictionary3;
		}

		public static List<Line> GroupLinesAndLink(List<ILineGeter> inputLines, uint inputUsePrecision)
		{
			XYZEqualityComparer xyzequalityComparer = new XYZEqualityComparer();
			xyzequalityComparer.SetPrecision(inputUsePrecision);
			Dictionary<XYZ, List<ILineGeter>> dictionary = new Dictionary<XYZ, List<ILineGeter>>(xyzequalityComparer);
			foreach (ILineGeter lineGeter in inputLines)
			{
				Line useLine = lineGeter.UseLine;
				LineGroupManger.AddDic(dictionary, useLine, lineGeter, 0);
				LineGroupManger.AddDic(dictionary, useLine, lineGeter, 1);
			}
			Dictionary<ILineGeter, List<XYZ>> dictionary2 = new Dictionary<ILineGeter, List<XYZ>>();
			foreach (KeyValuePair<XYZ, List<ILineGeter>> keyValuePair in dictionary)
			{
				foreach (ILineGeter lineGeter2 in keyValuePair.Value)
				{
					if (!dictionary2.ContainsKey(lineGeter2))
					{
						dictionary2.Add(lineGeter2, new List<XYZ>
						{
							null,
							null
						});
					}
					Line useLine2 = lineGeter2.UseLine;
					int index = 0;
					if (MathUtility.LessThan(keyValuePair.Key.DistanceTo(useLine2.GetEndPoint(1)), keyValuePair.Key.DistanceTo(useLine2.GetEndPoint(0))))
					{
						index = 1;
					}
					dictionary2[lineGeter2][index] = keyValuePair.Key;
				}
			}
			List<Line> list = new List<Line>();
			foreach (KeyValuePair<ILineGeter, List<XYZ>> keyValuePair2 in dictionary2)
			{
				Line line = keyValuePair2.Key.UseLine;
				foreach (XYZ xyz in keyValuePair2.Value)
				{
					if (xyz != null)
					{
						line = CurveUtilityMethod.AdjustLineToNearPoint(line, xyz);
					}
				}
				list.Add(line);
			}
			return list;
		}

		public static List<List<ILineGeter>> GroupLines(List<ILineGeter> inputLines, uint directionPrecision, double distanceEps)
		{
			List<List<ILineGeter>> list = new List<List<ILineGeter>>();
			XYZEqualityComparer usefirstGroupXYZComparer = new XYZEqualityComparer();
			usefirstGroupXYZComparer.SetPrecision(directionPrecision);
			foreach (IGrouping<int, ILineGeter> source in from k in inputLines
			group k by usefirstGroupXYZComparer.GetHashCode(XYZUtilityMethod.Abs(k.UseLine.Direction)))
			{
				list.AddRange(LineGroupManger.GroupSameLines(source.ToList<ILineGeter>(), Math.Abs(distanceEps)));
			}
			return list;
		}

		private static Line AdjustNoneLinkPoint(XYZEqualityComparer useComparer, ref KeyValuePair<XYZ, ILineGeter> InputKvp, XYZ usePoint)
		{
			Line result;
			try
			{
				if (CurveGroupManger.IfInputPointIsStartPoint(useComparer, InputKvp.Key, InputKvp.Value.UseLine))
				{
					if (useComparer.GetHashCode(usePoint) == useComparer.GetHashCode(InputKvp.Value.UseLine.GetEndPoint(1)))
					{
						result = InputKvp.Value.UseLine;
					}
					else
					{
						result = Line.CreateBound(usePoint, InputKvp.Value.UseLine.GetEndPoint(1));
					}
				}
				else if (useComparer.GetHashCode(usePoint) == useComparer.GetHashCode(InputKvp.Value.UseLine.GetEndPoint(0)))
				{
					result = InputKvp.Value.UseLine;
				}
				else
				{
					result = Line.CreateBound(InputKvp.Value.UseLine.GetEndPoint(0), usePoint);
				}
			}
			catch (Exception)
			{
				result = InputKvp.Value.UseLine;
			}
			return result;
		}

		private static List<List<ILineGeter>> GroupSameLines(List<ILineGeter> inputLineGeter, double eps)
		{
			List<List<ILineGeter>> list = new List<List<ILineGeter>>();
			List<ILineGeter> list2 = new List<ILineGeter>();
			list2.AddRange(inputLineGeter);
			List<ILineGeter> list3 = new List<ILineGeter>();
			for (int i = 0; i < list2.Count; i++)
			{
				list3 = new List<ILineGeter>();
				list3.Add(list2[i]);
				for (int j = i + 1; j < list2.Count; j++)
				{
					if (MathUtility.Lessthan_Or_Equal(LineGroupManger.GetDistanceBetweenLinesMid(list2[j], list3), eps))
					{
						list3.Add(list2[j]);
						list2.RemoveAt(j);
						j--;
					}
				}
				list.Add(list3);
			}
			return list;
		}

		private static double GetDistanceBetweenLinesMid(ILineGeter inputLine, List<ILineGeter> inputLines)
		{
			List<Line> list = new List<Line>();
			foreach (ILineGeter lineGeter in inputLines)
			{
				list.Add(lineGeter.UseLine.Clone() as Line);
			}
			foreach (Line line in list)
			{
				line.MakeUnbound();
			}
			XYZ xyz = inputLine.UseLine.Evaluate(0.5, true);
			List<double> list2 = new List<double>();
			foreach (Line line2 in list)
			{
				list2.Add(line2.Project(xyz).Distance);
			}
			double result = double.MaxValue;
			if (list2.Count > 0)
			{
				result = list2.Average();
			}
			return result;
		}

		private static void AddDic(Dictionary<XYZ, List<ILineGeter>> returnValue, Line useLine, ILineGeter oneLine, int usePointIndex)
		{
			if (!returnValue.ContainsKey(useLine.GetEndPoint(usePointIndex)))
			{
				returnValue.Add(useLine.GetEndPoint(usePointIndex), new List<ILineGeter>());
			}
			returnValue[useLine.GetEndPoint(usePointIndex)].Add(oneLine);
		}

		private static void CalculateTransformPoints(Dictionary<Line, Line> useBaseLineAndUnboundBaseLine, Dictionary<ILineGeter, Line> projectPair, Dictionary<XYZ, XYZ> usePointDic)
		{
			foreach (KeyValuePair<ILineGeter, Line> keyValuePair in projectPair)
			{
				ILineGeter key = keyValuePair.Key;
				Line value = keyValuePair.Value;
				Line line = useBaseLineAndUnboundBaseLine[value];
				for (int i = 0; i <= 1; i++)
				{
					XYZ endPoint = key.UseLine.GetEndPoint(i);
					XYZ xyz = usePointDic[endPoint];
					xyz = line.Project(xyz).XYZPoint;
					usePointDic[endPoint] = xyz;
				}
			}
		}

		private static Dictionary<ILineGeter, Line> CalculatePorjectLinePair(List<Line> lstBackBaseLine, List<ILineGeter> lstBackInput, double inputDistanceLimit, ref Dictionary<Line, Line> useBaseLineAndUnboundBaseLine, double? directionTolerance, bool? ifLimitProjectDirection = null)
		{
			Dictionary<ILineGeter, Line> dictionary = new Dictionary<ILineGeter, Line>();
			useBaseLineAndUnboundBaseLine = new Dictionary<Line, Line>();
			foreach (Line key in lstBackBaseLine)
			{
				useBaseLineAndUnboundBaseLine.Add(key, null);
			}
			foreach (ILineGeter lineGeter in lstBackInput)
			{
				int num = -1;
				double num2 = double.MaxValue;
				for (int i = 0; i < lstBackBaseLine.Count; i++)
				{
					Line line = lstBackBaseLine[i];
					Line value = useBaseLineAndUnboundBaseLine[line];
					double num3;
					if (LineGroupManger.IfLineCanProjectToBaseLine(lineGeter.UseLine, line, ref value, out num3, directionTolerance, ifLimitProjectDirection))
					{
						useBaseLineAndUnboundBaseLine[line] = value;
						if (MathUtility.LessThan(num3, num2))
						{
							num2 = num3;
							num = i;
						}
					}
				}
				if (-1 != num && MathUtility.Lessthan_Or_Equal(num2, inputDistanceLimit))
				{
					dictionary.Add(lineGeter, lstBackBaseLine[num]);
				}
			}
			return dictionary;
		}

		private static bool IfLineCanProjectToBaseLine(Line inputLine, Line inputBaseLine, ref Line useUnBoundBaseLine, out double distance, double? directionTolerance, bool? ifLimitProjectDirection = null)
		{
			distance = 0.0;
			if (directionTolerance == null && !XYZUtilityMethod.Abs(inputLine.Direction).IsAlmostEqualTo(XYZUtilityMethod.Abs(inputBaseLine.Direction)))
			{
				return false;
			}
			if (directionTolerance != null && !XYZUtilityMethod.Abs(inputLine.Direction).IsAlmostEqualTo(XYZUtilityMethod.Abs(inputBaseLine.Direction), directionTolerance.Value))
			{
				return false;
			}
			if (null == useUnBoundBaseLine)
			{
				useUnBoundBaseLine = (inputBaseLine.Clone() as Line);
				useUnBoundBaseLine.MakeUnbound();
			}
			double[] array = new double[2];
			double[] array2 = new double[2];
			for (int i = 0; i <= 1; i++)
			{
				array[i] = useUnBoundBaseLine.Project(inputLine.GetEndPoint(i)).Parameter;
				array2[i] = useUnBoundBaseLine.Project(inputBaseLine.GetEndPoint(i)).Parameter;
			}
			if (MathUtility.GreaterThan(array.Min(), array2.Max()) || MathUtility.GreaterThan(array2.Min(), array.Max()))
			{
				return false;
			}
			XYZ xyz = inputLine.Evaluate(0.5, true);
			XYZ xyzpoint = useUnBoundBaseLine.Project(xyz).XYZPoint;
			if (ifLimitProjectDirection != null)
			{
				XYZ xyz2 = (xyzpoint - xyz).Normalize();
				XYZ direction = inputLine.Direction;
				XYZ xyz3 = new XYZ(-direction.Y, direction.X, direction.Z);
				if (ifLimitProjectDirection.Value && !xyz2.IsAlmostEqualTo(xyz3))
				{
					return false;
				}
				if (!ifLimitProjectDirection.Value && !xyz2.IsAlmostEqualTo(-xyz3))
				{
					return false;
				}
			}
			distance = xyzpoint.DistanceTo(xyz);
			return true;
		}

		private static List<ICurveGeter> TransformCurveGeter(List<ILineGeter> lstInputLineGeter, out Dictionary<ICurveGeter, ILineGeter> useDic)
		{
			List<ICurveGeter> list = new List<ICurveGeter>();
			useDic = new Dictionary<ICurveGeter, ILineGeter>();
			foreach (ILineGeter lineGeter in lstInputLineGeter)
			{
				CurveGeterBean curveGeterBean = new CurveGeterBean
				{
					UseCurve = lineGeter.UseLine
				};
				list.Add(curveGeterBean);
				useDic.Add(curveGeterBean, lineGeter);
			}
			return list;
		}
	}
}
